/*
	File:		Game.cpp
	Course:		SGP 1303
	Author:		Jmullens
	Purpose:	CGame class controls the SGD Wrappers
				and the game state machine
*/


#include "Game.h"

#include "../CTimer.h"
////////////////////////////////////////////////////
// SGD Wrappers
#include "../SGD_Wrappers/CSGD_Direct3D.h"
#include "../SGD_Wrappers/CSGD_DirectInput.h"
#include "../SGD_Wrappers/CSGD_TextureManager.h"
#include "../SGD_Wrappers/CSGD_XAudio2.h"

// States
#include "States\IGameState.h"
#include "States\MainMenuState.h"
#include "States\GamePlayState.h"
#include "States\PauseState.h"
#include "States\SettingState.h"
#include <iostream>
using namespace std;
#include <fstream>

#include "Animations\AnimationManager.h"
#include "SoundSystem\SoundSystem.h"
#include "Particle System\ParticleManager.h"

////////////////////////////////////////////////////
// SINGLETON:
//	- instantiate the static data member
/*static*/	CGame*	CGame::s_Instance = nullptr;

/*static*/	CGame*	CGame::GetInstance( void )
{
	// Allocate the data member if necessary
	if( s_Instance == nullptr )
		s_Instance = new CGame;

	// Return the instance
	return s_Instance;
}

/*static*/	void	CGame::DeleteInstance( void )
{
	// Deallocate the data member
	if( s_Instance != nullptr )
	{
		delete s_Instance;
		s_Instance = nullptr;
	}
}


////////////////////////////////////////////////////
// Constructor
CGame::CGame( void )
{
	// Set the data members to 'safe' values
	m_pD3D	= nullptr;
	m_pDI	= nullptr;
	m_pTM	= nullptr;
	m_pXA	= nullptr;
	
	m_nScreenWidth	= 1;
	m_nScreenHeight	= 1;
	m_bIsWindowed	= true;

	m_pCurrStates.clear();
	m_nStateIndex = -1;

	m_dwCurrTime = 0;
	
	

}

////////////////////////////////////////////////////
// Destructor
CGame::~CGame(void)
{
	CAnimationManager::GetInstance()->ClearMemory();
}

////////////////////////////////////////////////////
// Initialize
//	- setup the wrappers
void CGame::Initialize( HWND hWnd, HINSTANCE hInstance, 
					int nWidth, int nHeight,
					bool bWindowed )
{
	// Store the SGD Wrapper singletons
	m_pD3D	= CSGD_Direct3D::GetInstance();
	m_pDI	= CSGD_DirectInput::GetInstance();
	m_pTM	= CSGD_TextureManager::GetInstance();
	m_pXA	= CSGD_XAudio2::GetInstance();
	
	// Store the parameters
	m_nScreenWidth	= nWidth;
	m_nScreenHeight	= nHeight;
	//m_bIsWindowed	= bWindowed;



	// Initialize the SGD Wrappers
	m_pD3D->InitDirect3D( hWnd, m_nScreenWidth, m_nScreenHeight, m_bIsWindowed, false );
	
	m_pDI->InitDirectInput( hWnd, hInstance, DI_KEYBOARD | DI_MOUSE, DI_MOUSE );
	
	m_pTM->InitTextureManager( m_pD3D->GetDirect3DDevice(), m_pD3D->GetSprite() );

	m_pXA->InitXAudio2();

	//Animation Manager
	CAnimationManager::GetInstance()->LoadAnimationsXML();
	m_nCursorID = m_pTM->LoadTexture(_T("resource/graphics/Cursor.png"),0);

	//Store the current time
	m_dwCurrTime = GetTickCount();

	// Start in the play state
	ChangeState( CMainMenuState::GetInstance() );

	ifstream fin;

	fin.open("Settings.txt",ios_base::in);

	if(fin.good())
		{
			float fSound;
			int Window;
			fin >> fSound;
			SoundSystem::GetInstance()->SetVolume(fSound);
			fin.ignore(INT_MAX, '\n');
			fin >> Window;
			if(Window == 1)
				SetWindow(true);
			else
				SetWindow(false);
		}

	fin.close();

}


////////////////////////////////////////////////////
// Main
//	- play one frame of the game
bool CGame::Main( void )
{
	// Handle input
	if( Input() == false )
		return false;

	// Update the game
	Update();

	// Render the game
	Render();
	return true;
}



////////////////////////////////////////////////////
// Shutdown
//	- cleanup the current state & wrappers
void CGame::Shutdown( void )
{
	// Shutdown the current state
	ChangeState( nullptr );

	CAnimationManager::GetInstance()->ClearMemory();

	// Shutdown the SGD Wrappers in REVERSE-ORDER!
	if( m_pXA != nullptr )
	{
		m_pXA->ShutdownXAudio2();
		m_pXA = nullptr;
	}

	if( m_pTM != nullptr )
	{
		m_pTM->ShutdownTextureManager();
		m_pTM = nullptr;
	}

	if( m_pDI != nullptr )
	{
		m_pDI->ShutdownDirectInput();
		m_pDI = nullptr;
	}

	if( m_pD3D != nullptr )
	{
		m_pD3D->ShutdownDirect3D();
		m_pD3D = nullptr;
	}

	if(CAnimationManager::GetInstance() != nullptr)
	{
		CAnimationManager::GetInstance()->ClearMemory();
	}
}



////////////////////////////////////////////////////
// Input
//	- read input & global processing
bool CGame::Input( void )
{
	// Read the input hardware
	m_pDI->ReadDevices();
	//POINT mouse = {0,0};
	//GetCursorPos(&mouse);
	//m_pDI->MouseSetPosX(mouse.x);
	//m_pDI->MouseSetPosY(mouse.y);

	//POINT mouse = {0,0};
	//GetCursorPos(&mouse);
	//m_pDI->MouseSetPosX(mouse.x);
	//m_pDI->MouseSetPosY(mouse.y);

	// Check if the current state is valid
	if( m_pCurrStates[m_nStateIndex] == nullptr )
		return false;


	// Let the current handle the input
	return m_pCurrStates[m_nStateIndex]->Input();
}


////////////////////////////////////////////////////
// Update
//	- refresh audio
void CGame::Update( void )
{
	// Refresh the audio
	m_pXA->Update();

	//Calculate the elasped time between frames 
	DWORD dwNow = GetTickCount();

	float fElaspedTime = (dwNow - m_dwCurrTime) / 1000.0f;

	m_dwCurrTime = dwNow;

	CTimer::Update();
	// Update the current state
	m_pCurrStates[m_nStateIndex]->Update(fElaspedTime);
}


////////////////////////////////////////////////////
// Render
//	- prepare the D3D wrapper
void CGame::Render( void )
{
	// Clear the background
	m_pD3D->Clear( 255, 0, 0 );

	// Start the rendering
	m_pD3D->DeviceBegin();
	m_pD3D->SpriteBegin();

	
	// Render the current state
	m_pCurrStates[m_nStateIndex]->Render();

	m_pTM->Draw(m_pTM->LoadTexture(_T("resource/graphics/Cursor.png"),0),m_pDI->MouseGetPosX(),m_pDI->MouseGetPosY(),64.0f/GetHeight(),64.0f/GetHeight());

	// Stop the rendering
	m_pD3D->SpriteEnd();
	m_pD3D->DeviceEnd();


	// Present the backbuffer to the screen
	m_pD3D->Present();
}


////////////////////////////////////////////////////
// ChangeState
//	- change the current state, loading & unloading as needed
void CGame::ChangeState( IGameState* pNewState )
{
	// Exit the current state
	for( int i = m_nStateIndex; i >= 0; i-- )
	{
		if( m_pCurrStates[i] != nullptr )
			m_pCurrStates[i]->Exit();
	}

	m_pCurrStates.clear();

	// Store the parameter
	m_pCurrStates.push_back(pNewState);
	m_nStateIndex = 0;

	// Enter the new state
	if( m_pCurrStates[m_nStateIndex] != nullptr )
		m_pCurrStates[m_nStateIndex]->Enter();
}


void	CGame::SetWindow( bool b )
{
	m_bIsWindowed = b;

	m_pD3D->ChangeDisplayParam( m_nScreenWidth, m_nScreenHeight, m_bIsWindowed );
}


void CGame::AddState( IGameState* pNewState )
{
	if(pNewState==nullptr)
		return;

	m_pCurrStates.push_back( pNewState );
	m_nStateIndex++;

	if( m_pCurrStates[m_nStateIndex] != nullptr )
		m_pCurrStates[m_nStateIndex]->Enter();
}

void CGame::PopState()
{
	if(m_pCurrStates.size()==0)
		return;

	if( m_pCurrStates[m_nStateIndex] != nullptr )
		m_pCurrStates[m_nStateIndex]->Exit();
	
	m_pCurrStates.pop_back();
	m_nStateIndex--;
}